﻿using FlyingCircusWebIS.Models;
using System;
using System.Collections.Generic;
using System.Data.Common;
using System.Data.OleDb;
using System.Linq;
using System.Web;

namespace FlyingCircusWebIS.Models
{
    public class PessoaService: IPessoaService
    {
        private IPessoaDAO pessoaDAO;
        private IEnderecoDAO enderecoDAO;
        private ITelefoneDAO telefoneDAO;
        private IMidiaSocialDAO midiaSocialDAO;
        private VooService vooService;
        private ReservaService reservaService;

        public PessoaService()
        {
            pessoaDAO = new PessoaDAO();
            enderecoDAO = new EnderecoDAO();
            telefoneDAO = new TelefoneDAO();
            midiaSocialDAO = new MidiaSocialDAO();
            reservaService = new ReservaService();
            vooService = new VooService();

        }


        public Pessoa AutenticarComoUsuario(string usuario, string senha)
        {
            return pessoaDAO.Listar().Find(
                        m => (m.UsuarioSistema.Equals(usuario) || m.Email.Equals(usuario)) && m.SenhaSistema.Equals(senha) && m.CadastroAtivo.Equals("S"));
        }


        public List<ValidationResult> ValidarAutenticacaoUsuario(string usuario, string senha)
        {
            List<ValidationResult> erros = new List<ValidationResult>();

            Pessoa pessoa = pessoaDAO.Listar().Find(
                        m => (m.UsuarioSistema.Equals(usuario) || m.Email.Equals(usuario)) && m.SenhaSistema.Equals(senha));

            if (pessoa == null)
            {
                erros.Add(new ValidationResult("Usuário e/ou senha inválidos."));
            }  
            else
            {
                if (pessoa.CadastroAtivo.Equals("N"))
                    erros.Add(new ValidationResult(
                        "Ative o seu cadastro através do link fornecido no email de ativação. Caso não tenha recebido o email ou algum outro problema tenha acontecido, contate o suporte."));
            }


            return erros;
        }

        public List<Pessoa> ListarPessoas()
        {
            return pessoaDAO.Listar();
        }


        public Pessoa BuscarPessoaPorId(int id)
        {
            Pessoa registro = pessoaDAO.BuscarPorId(id);
            return registro;
        }


        public List<Pessoa> ListarPessoasFiltro(string nome, string email, string cpf, string sexo)
        {
            List<Pessoa> encontrados = pessoaDAO.Listar().FindAll(p => 
                        p.NomeCompleto.StartsWith(nome, StringComparison.InvariantCultureIgnoreCase) &&
                        p.Email.StartsWith(email, StringComparison.InvariantCultureIgnoreCase) &&
                        p.CPF.StartsWith(cpf, StringComparison.InvariantCultureIgnoreCase) &&
                        p.Sexo.StartsWith(sexo, StringComparison.InvariantCultureIgnoreCase));

            return encontrados;  
        }

        public List<ValidationResult> ValidarEntidadePessoa(Pessoa entidade, bool edicao)
        {
            List<ValidationResult> erros = new List<ValidationResult>();

            if (edicao)
            {
                List<ValidationResult> errosAtualizar = ValidaAtualizarPessoa(entidade);
                if (errosAtualizar != null)
                    erros.AddRange(errosAtualizar);
            }
            else
            {
                List<ValidationResult> errosAdicionar = ValidaAtualizarPessoa(entidade);
                if (errosAdicionar != null)
                    erros.AddRange(errosAdicionar);
            }

            return erros;
        }

        public List<ValidationResult> ValidaAdicionarPessoa(Pessoa entidade)
        {
            List<ValidationResult> erros = new List<ValidationResult>();

            bool existeUsuario = pessoaDAO.Listar().Any(
                p => p.UsuarioSistema.Equals(entidade.UsuarioSistema, StringComparison.InvariantCultureIgnoreCase));

            if (existeUsuario)
                erros.Add(new ValidationResult("Usuário já cadastrado. Escolha outro."));

            bool existeEmail = pessoaDAO.Listar().Any(
                p => p.Email.Equals(entidade.Email, StringComparison.InvariantCultureIgnoreCase));

            if (existeEmail)
                erros.Add(new ValidationResult("Email já cadastrado. Escolha outro."));

            bool existeCPF = pessoaDAO.Listar().Any(
                p => p.CPF.Equals(entidade.CPF));

            if (existeCPF)
                erros.Add(new ValidationResult("Já existe uma pessoa com o CPF informado."));

            
            return erros;
        }


        public void Adicionar(Pessoa pessoa, Endereco endereco, List<Telefone> telefones)
        {
            /**
             * O processo de cadastro de pessoa deve estar embutido numa transação,
             * pois várias tabelas(Pessoa, Endereço, Telefone) estão envolvidas.
             * Além disso existe o envio do email de ativação do cadastro.
             */ 
            DbTransaction transacao = GerenciadorConexao.Conexao.BeginTransaction();
            try
            {
                int idEndereco = ProximoIdEndereco(transacao);
                endereco.IdEndereco = idEndereco;
                AdicionarEndereco(endereco, transacao);

                int idPessoa = ProximoIdPessoa(transacao);
                pessoa.IdPessoa = idPessoa;
                pessoa.IdEndereco = idEndereco;
                AdicionarPessoa(pessoa, transacao);

                foreach (var telefone in telefones)
                {
                    telefone.IdPessoa = idPessoa;
                    AdicionarTelefone(telefone, transacao);
                }

                string email = pessoa.Email.Trim();
                string nome = pessoa.NomeCompleto.Trim();
                EnviarEmailAtivacao(email, nome);


                // Se chegar até aqui deu tudo certo
                // então realiza o Commit 
                transacao.Commit();
            }
            catch (Exception)
            {
                transacao.Rollback();
                throw;
            }

        }

        private void EnviarEmailAtivacao(string emailCliente, string nomeCliente)
        {
            MidiaSocial midiaSocial = midiaSocialDAO.Ler();
            midiaSocial.EnviarEmailDeAtivacaoDeCadastro(emailCliente, nomeCliente);
        }

        public List<ValidationResult> ValidaAtualizarPessoa(Pessoa entidade)
        {
            List<ValidationResult> erros = new List<ValidationResult>();

            bool existeUsuario = pessoaDAO.Listar().Any(
                p => p.UsuarioSistema.Equals(entidade.UsuarioSistema, StringComparison.InvariantCultureIgnoreCase) && p.IdPessoa != entidade.IdPessoa);
            
            if (existeUsuario)
                erros.Add(new ValidationResult("Usuário já cadastrado. Escolha outro."));

            bool existeEmail = pessoaDAO.Listar().Any(
                p => p.Email.Equals(entidade.Email, StringComparison.InvariantCultureIgnoreCase) && p.IdPessoa != entidade.IdPessoa);

            if (existeEmail)
                erros.Add(new ValidationResult("Email já cadastrado. Escolha outro."));


            bool existeCPF = pessoaDAO.Listar().Any(
                p => p.CPF.Equals(entidade.CPF) && p.IdPessoa != entidade.IdPessoa);

            if (existeCPF)
                erros.Add(new ValidationResult("Já existe uma pessoa com o CPF informado."));

            return erros;
        }


        public void AdicionarPessoa(Pessoa pessoa, DbTransaction transacao)
        {
            pessoaDAO.Adicionar(pessoa, transacao);
        }

        public void AtualizarPessoa(Pessoa entidade, DbTransaction transacao)
        {
            pessoaDAO.Adicionar(entidade, transacao);
        }

        public List<ValidationResult> ValidaRemoverPorId(int id)
        {

            var erros = new List<ValidationResult>();


            bool temDependenciaReserva = reservaService.Listar().Any(e => e.IdPessoaCliente == id);
            if (temDependenciaReserva)
                erros.Add(new ValidationResult("DependenciaReserva", "O registro tem dependência no contexto de reservas."));    

            bool temDependenciaVoo = vooService.Listar().Any(e => e.IdPessoaUsuario == id);
            if (temDependenciaVoo)
                erros.Add(new ValidationResult("DependenciaVoo", "O registro tem dependência no contexto de voos."));    



            return erros;
            
        }

        public void RemoverPessoaPorId(int id)
        {
            Pessoa pessoa = pessoaDAO.BuscarPorId(id);

            /**
             * A ação de remover uma pessoa precisa estar associada a uma transação,
             * pois é preciso remover os telefones e o endereço relacionados a esta pessoa.
             */
            OleDbTransaction transacao = GerenciadorConexao.Conexao.BeginTransaction();
            try
            {
                int idEndereco = pessoa.IdEndereco;
                
                telefoneDAO.RemoverPorIdPessoa(id, transacao);
                pessoaDAO.RemoverPorId(id, transacao);
                enderecoDAO.RemoverPorId(idEndereco, transacao);

                transacao.Commit();
            }
            catch (Exception)
            {
                transacao.Rollback();
                throw;
            }
        }

        public int ProximoIdPessoa(DbTransaction transacao)
        {
            return pessoaDAO.ProximoIdPessoa(transacao);
        }

        public void AdicionarEndereco(Endereco entidade, DbTransaction transacao)
        {
            enderecoDAO.Adicionar(entidade, transacao);
        }

        public void AtualizarEndereco(Endereco entidade, DbTransaction transacao)
        {
            enderecoDAO.Atualizar(entidade, transacao);
        }

        public Endereco BuscarEnderecoPorId(int id)
        {
            return enderecoDAO.BuscarPorId(id);
        }

        public int ProximoIdEndereco(DbTransaction transacao)
        {
            return enderecoDAO.ProximoIdEndereco(transacao);
        }

        public void AdicionarTelefone(Telefone entidade, DbTransaction transacao)
        {
            telefoneDAO.Adicionar(entidade, transacao);
        }

        public void AtualizarTelefone(Telefone entidade, DbTransaction transacao)
        {
            telefoneDAO.Atualizar(entidade, transacao);
        }

        public Telefone BuscarTelefonePorId(int id)
        {
            return telefoneDAO.BuscarPorId(id);
        }

        public int ProximoIdTelefone(DbTransaction transacao)
        {
            return telefoneDAO.ProximoIdTelefone(transacao);
        }



        public List<Telefone> ListarTelefonesPorIdPessoa(int id)
        {
            List<Telefone> telefonesEncontados = telefoneDAO.Listar().FindAll(t => t.IdPessoa == id);
            return telefonesEncontados;
        }


        public bool AtivarCad(string cod)
        {
            string emailInvertido = cod.Replace("veAihMarot", "");
            
            char[] arrayEmail = emailInvertido.ToCharArray();
            Array.Reverse(arrayEmail);

            string email = new String(arrayEmail);

            Pessoa regPessoa = pessoaDAO.Listar().Find(p => p.Email.Equals(email));

            if (regPessoa != null)
            {
                pessoaDAO.AtivarCadastro(regPessoa.IdPessoa);
                return true;
            }

            return false;
        }


        public bool AtivarCadAdministracao(int id)
        {
            Pessoa pessoa = pessoaDAO.BuscarPorId(id);
            if (pessoa != null)
            {
                pessoaDAO.AtivarCadastro(id);
                return true;
            }
            else
            {
                return false;
            }
            

        }


        public bool DesativarCadAdministracao(int id)
        {
            Pessoa pessoa = pessoaDAO.BuscarPorId(id);
            if (pessoa != null)
            {
                pessoaDAO.DesativarCadastro(id);
                return true;
            }
            else
            {
                return false;
            }

        }


        public void AtribuirDescontoParaPessoa(int idPessoa, decimal desconto)
        {
            Pessoa pessoa = pessoaDAO.BuscarPorId(idPessoa);
            pessoa.DescontoEspecial = "S";
            pessoa.PorcDescontoEspecial = desconto;
            pessoaDAO.Atualizar(pessoa, null);
        }


        public bool RemoverDescontoEspecial(int id)
        {
            Pessoa pessoa = pessoaDAO.BuscarPorId(id);
            if (pessoa != null)
            {
                pessoa.DescontoEspecial = "N";
                pessoa.PorcDescontoEspecial = Decimal.Zero;
                pessoaDAO.Atualizar(pessoa, null);
                return true;
            }
            else
            {
                return false;
            }

        }


        public bool CancelarRecimentoMarketing(int id)
        {
            Pessoa pessoa = pessoaDAO.BuscarPorId(id);
            if (pessoa != null)
            {
                pessoa.RecebeInfoMarketing = "N";
                pessoaDAO.Atualizar(pessoa, null);
                return true;
            }
            else
            {
                return false;
            }
        }

        public bool AtivarRecebimentoMarketing(int id)
        {
            Pessoa pessoa = pessoaDAO.BuscarPorId(id);
            if (pessoa != null)
            {
                pessoa.RecebeInfoMarketing = "S";
                pessoaDAO.Atualizar(pessoa, null);
                return true;
            }
            else
            {
                return false;
            }
        }


        public bool RevogarDireitoAdministrativo(int id)
        {
            Pessoa pessoa = pessoaDAO.BuscarPorId(id);
            if (pessoa != null)
            {
                pessoa.NivelPessoa = "C";
                pessoaDAO.Atualizar(pessoa, null);
                return true;
            }
            else
            {
                return false;
            }
        }

        public bool ConcederDireitoAdministrativo(int id)
        {
            Pessoa pessoa = pessoaDAO.BuscarPorId(id);
            if (pessoa != null)
            {
                pessoa.NivelPessoa = "A";
                pessoaDAO.Atualizar(pessoa, null);
                return true;
            }
            else
            {
                return false;
            }
        }


    }
}